﻿//This filter is tested and seems to work correctly

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// Most scanned images contains noise in form of darker dots and disturbances
    /// caused by the scanning process. If these are not removed before the feature
    /// extraction and classification, the image may mistakenly be interpreted wrong.
    /// Salt and Pepper noise removal does just what we desire; it erases the black
    /// dots, called the Pepper, and it also fills in holes in the image, called Salt.
    /// </summary>
    public class NoiseRemoval : BaseImageFilter
    {
        UnsafeBitmap IntensityImage = null;

        /// <summary>
        /// default constructor
        /// </summary>
        public NoiseRemoval()
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="_IntensityImage"></param>
        public NoiseRemoval(UnsafeBitmap _IntensityImage)
        {
            IntensityImage = _IntensityImage;
        }

        /// <summary>
        /// used by clone to create a deep copy
        /// </summary>
        /// <param name="_noiseRemoval"></param>
        internal NoiseRemoval(NoiseRemoval _noiseRemoval)
        {
            this.IntensityImage = _noiseRemoval.IntensityImage;
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            IntensityImage = _bitmap;
            /*It removes the pixel
             * that is stood alone any where in the 
             * vicinity.
             *It is found to be accurate 4 our System.
             * */

            UnsafeBitmap b2 = (UnsafeBitmap)IntensityImage.Clone();
            byte value;
            
            // GDI+ still lies to us - the return format is BGR, NOT RGIntensityImage.

            int nIntensityHeight = IntensityImage.Height;
            int nIntensityWidth = IntensityImage.Width;

            BitmapData bmData = IntensityImage.BitmapData; //IntensityImage.LockBits(new Rectangle(0, 0, nIntensityWidth, nIntensityHeight),ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

            BitmapData bmData2 = b2.BitmapData; //b2.LockBits(new Rectangle(0, 0, nIntensityWidth, nIntensityHeight),ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

            int stride = bmData.Stride;
            IntPtr Scan0 = bmData.Scan0;
            IntPtr Scan02 = bmData2.Scan0;

            unsafe
            {
                byte* p = (byte*)(void*)Scan0;
                byte* p2 = (byte*)(void*)Scan02;

                int nBytesPerPixelCount = _bitmap.BytesPerPixelCount;

                int nOffset = stride - nIntensityWidth * 3;
                int nWidth = nIntensityWidth * 3;

                //int nPixel=0;

                p += stride;
                p2 += stride;
                //int val;
                for (int y = 1; y < nIntensityHeight - 1; ++y)
                {
                    p += 3;
                    p2 += 3;

                    for (int x = 3; x < nWidth - 3; ++x)
                    {
                        value = p2[0];
                        if (value == 0)
                        {
                            if ((p2 + 3)[0] == 0 || (p2 - 3)[0] == 0 || (p2 + stride)[0] == 0 || (p2 - stride)[0] == 0 || (p2 + stride + 3)[0] == value || (p2 + stride - 3)[0] == 0 || (p2 - stride - 3)[0] == 0 || (p2 + stride + 3)[0] == 0)
                            {
                                p[0] = value;
                            }
                            else
                            {
                                p[0] = 255;
                            }
                        }
                        ++p;
                        ++p2;
                    }

                    p += nOffset + nBytesPerPixelCount; //nOffset + 3;
                    p2 += nOffset + nBytesPerPixelCount; //nOffset + 3;
                }
            }

            b2.Dispose();
            return _bitmap;
        }

        /// <summary>
        /// create a deep copy
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new NoiseRemoval(this);
        }
    }
}
